<?php
// 应用公共文件

use think\facade\Request;
use think\facade\Config;
use app\system\model\Meta;

/**
 * 获取网站域名
 * @return string 返回域名
 */
define('DOMAIN', Request::domain());

/**
 * 获取树形 Html Select Options
 * @param $data array 树形数据
 * @param int $pid 父级ID
 * @param string $prefix 前缀
 * @param int $selected 选中ID
 * @return string 返回HTML
 */
function generateOptions($data, $pid = 0, $prefix = '', $selected = 0)
{
    $options = '';
    foreach ($data as $value) {
        if ($value['pid'] == $pid) {
            $selectedStr = '';
            if ($selected && $selected == $value['id']) {
                $selectedStr = 'selected';
            }
            $options .= "<option value=\"{$value['id']}\" " . $selectedStr . ">{$prefix}{$value['name']}</option>";
            $options .= generateOptions($data, $value['id'], $prefix . '--', $selected);
        }
    }
    return $options;
}

/**
 * 获取树形数组
 * @param $data array 树形数据
 * @param int $pid 父级ID
 * @param array $selected 选中ID
 * @return array 返回数组
 */
function generateTree($data, $pid = 0, $selected = [])
{
    $arr = [];
    foreach ($data as $value) {
        if ($value['pid'] == $pid) {
            $value['children'] = generateTree($data, $value['id'], $selected);
            $state = ['opened' => 'true'];
            if ($selected && in_array($value['id'], $selected)) {
                $state['selected'] = 'true';
            }
            $query = [
                'id' => $value['id'],
                'text' => $value['name'],
                'state' => $state,
                'children' => $value['children'],
                'icon' => $value['children'] ? 'mdi mdi-folder-outline' : 'mdi mdi-file-outline',
            ];
            if (isset($value['status'])) {
                $query['status'] = $value['status'];
            }
            $arr[] = $query;
        }
    }
    return $arr;
}

/**
 * 排序
 * @param $a array 数组A
 * @param $b array 数组B
 * @return int 返回排序结果
 */
function compareBySort($a, $b)
{
    if ($a['sort'] == $b['sort']) {
        return 0;
    }
    return ($a['sort'] < $b['sort']) ? -1 : 1;
}

/**
 * 单图片上传选择器
 * @param $name string 表单name
 * @param $label string 表单label
 * @param $tip string 表单提示
 * @param bool $require 是否必填
 * @param int $width 表单col宽度
 * @return string 返回HTML
 */
function chooseOneImage($name, $label, $tip, $require = false, $width = 2)
{
    $last = 12 - $width;
    $url = url('attachment/index/index')->build() . '?iframe=1&callback=receiveDataFor' . $name;
    $red = $require ? '<font color="red">*</font>' : '';
    $script = <<<EOF
<script>
var {$name}LayerIndex;
var {$name}AddBtn = `<div onclick="chooseImageFor{$name}();" style="cursor: pointer; width: 50px; height: 50px; background: #f8f8f8; display: flex; align-items: center; justify-content: center;">
    <span class="mdi mdi-camera-outline" style="font-size: 18px; color: gray;"></span>
</div>`;
var {$name}DeleteBtn = `<div class="delete-btn" style="position: absolute; display: none; top: 0; right: 0; left: 0; bottom: 0; background: rgba(0, 0, 0, .6); z-index: 999; text-align: center; padding-top: 10px;" onclick="deleteImageFor{$name}();"><span class="mdi mdi-trash-can-outline" style="font-size: 18px; color: white; opacity: 0.5"></span></div>`;
function chooseImageFor{$name}(){
    {$name}LayerIndex = open_layer('选择图片', '{$url}', function (){});
}
function receiveDataFor{$name}(url){
    $('#{$name}').val(url);
    $('#{$name}-url').html(`<div onmouseover="showDeleteIconFor{$name}($(this));" onmouseout="hideDeleteIconFor{$name}($(this));" style="cursor: pointer; position: relative;"><img src="` + url + `" style="height: 50px; width: auto; display: block; border: 1px solid #f1f1f1; padding: 1px;" />` + {$name}DeleteBtn + `</div>`);
    layer.close({$name}LayerIndex);
}
function showDeleteIconFor{$name}(e){
    e.find('.delete-btn').show();
}
function hideDeleteIconFor{$name}(e){
    e.find('.delete-btn').hide();
}
function deleteImageFor{$name}(){
    $('#{$name}-url').html({$name}AddBtn);
    $('#{$name}').val('');
 }
$(function (){
    $('#{$name}-url').html({$name}AddBtn);
});
</script>
EOF;
    return <<<EOF
<div class="mb-3 row">
    <label for="{$name}" class="col-sm-{$width} col-form-label">{$label} {$red}</label>
    <div class="col-sm-{$last}">
        <input type="hidden" name="{$name}" id="{$name}" value="">
        <div id="{$name}-url" style="display: flex; flex-direction: row; justify-content: flex-start;"></div>
        <div style="font-size: 12px; color: gray; margin-top: 5px;">{$tip}</div>
    </div>
</div>
{$script}
EOF;
}

/**
 * 多图片上传选择器
 * @param $name string 表单name
 * @param $label string 表单label
 * @param $tip string 表单提示
 * @param bool $require 是否必填
 * @param int $width 表单col宽度
 * @return string 返回HTML
 */
function chooseManyImage($name, $label, $tip, $require = false, $width = 2)
{
    $last = 12 - $width;
    $url = url('attachment/index/index')->build() . '?iframe=1&multiple=1&callback=receiveDataFor' . $name;
    $red = $require ? '<font color="red">*</font>' : '';
    $script = <<<EOF
<script>
var {$name}LayerIndex;
var {$name}AddBtn = `<div onclick="chooseImageFor{$name}();" style="cursor: pointer; width: 50px; height: 50px; background: #f8f8f8; display: flex; align-items: center; justify-content: center;">
    <span class="mdi mdi-camera-outline" style="font-size: 18px; color: gray;"></span>
</div>`;
function addPreviewFor{$name}(url){
    var {$name}DeleteBtn = `<div class="delete-btn" style="position: absolute; display: none; top: 0; right: 0; left: 0; bottom: 0; background: rgba(0, 0, 0, .6); z-index: 999; text-align: center; padding-top: 10px;" onclick="deleteImageFor{$name}($(this), '` + url + `');"><span class="mdi mdi-trash-can-outline" style="font-size: 18px; color: white; opacity: 0.5"></span></div>`;
    return `<div class="sortable" onmouseover="showDeleteIconFor{$name}($(this));" onmouseout="hideDeleteIconFor{$name}($(this));" style="cursor: pointer; position: relative; margin-right: 5px;"><img src="` + url + `" style="height: 50px; width: auto; display: block; border: 1px solid #f1f1f1; padding: 1px;" />` + {$name}DeleteBtn + `</div>`;
}
function chooseImageFor{$name}(){
    {$name}LayerIndex = open_layer('选择图片', '{$url}', function (){});
}
function receiveDataFor{$name}(url){
    var old = $('#{$name}').val();
    var arr = url.split(',');
    var html = '';
    for (var i = 0; i < arr.length; i++){
        html+= addPreviewFor{$name}(arr[i]);
    }
    $('#{$name}-url').append(html);
    if (old){
        $('#{$name}').val(old + ',' + url);
    }else{
        $('#{$name}').val(url);
    }
    layer.close({$name}LayerIndex);
    $('.sortable').arrangeable({dragEndEvent: '{$name}DragEnd'});
}
function showDeleteIconFor{$name}(e){
    e.find('.delete-btn').show();
}
function hideDeleteIconFor{$name}(e){
    e.find('.delete-btn').hide();
}
function deleteImageFor{$name}(e, url){
    var arr = $('#{$name}').val().split(',');
    var index = arr.indexOf(url);
    if (index > -1){
        arr.splice(index, 1);
    }
    $('#{$name}').val(arr.join(','));
    e.parent().remove();
 }
$(function (){
    $('#add-{$name}-button').html({$name}AddBtn);
    $(document).bind('{$name}DragEnd', function () {
        var arr = [];
        $('#{$name}-url').find('.sortable').each(function (){
            var url = $(this).find('img').attr('src');
            arr.push(url);
        });
        $('#{$name}').val(arr.join(','));
    });
});
</script>
EOF;
    return <<<EOF
<div class="mb-3 row">
    <label for="{$name}" class="col-sm-{$width} col-form-label">{$label} {$red}</label>
    <div class="col-sm-{$last}">
        <input type="hidden" name="{$name}" id="{$name}" value="">
        <div style="display: flex; flex-direction: row; justify-content: flex-start;">
            <div id="{$name}-url" style="display: flex; flex-direction: row; justify-content: flex-start;"></div>
            <div id="add-{$name}-button"></div>
        </div>
        <div style="font-size: 12px; color: gray; margin-top: 5px;">{$tip}</div>
    </div>
</div>
{$script}
EOF;
}

/**
 * 面包屑导航
 * @param $menus array 面包屑数组
 * @return string 返回HTML
 */
function breadcrumb($menus)
{
    $home = url('admin/dashboard/index')->build();
    $loop = '';
    foreach ($menus as $menu) {
        $arr = explode(':', $menu);
        if (isset($arr[1])) {
            $url = url($arr[1])->build();
            $loop .= '<li class="breadcrumb-item"><a href="' . $url . '">' . $arr[0] . '</a></li>';
        } else {
            $loop .= '<li class="breadcrumb-item active" aria-current="page">' . $arr[0] . '</li>';
        }
    }
    return <<<EOF
<nav aria-label="breadcrumb">
    <ol class="breadcrumb">
        <li class="breadcrumb-item"><a href="{$home}">首页</a></li>
        {$loop}
    </ol>
</nav>
EOF;
}

/**
 * 引用js常用插件
 * @param array $arr 插件数组
 * @param bool $line 返回类型 true: 返回字符串 false: 返回数组
 * @return array|string 返回数组或字符串
 */
function addJs($arr = [], $line = false)
{
    $js = [
        'jstree' => ['/js/jstree/style.min.css', '/js/jstree/jstree.min.js'],
        'select2' => ['/js/select2/select2.min.css', '/js/select2/select2.min.js', '/js/select2/zh-CN.js'],
        'tagsinput' => ['/js/jquery-tagsinput/jquery.tagsinput.min.css', '/js/jquery-tagsinput/jquery.tagsinput.min.js'],
        'webuploader' => ['/js/webuploader/webuploader.css', '/js/webuploader/webuploader.js'],
        'cookie' => ['/js/jquery.cookie.min.js'],
        'chart' => ['/js/chart.min.js'],
        'magnific-popup' => ['/js/magnific-popup/magnific-popup.min.css', '/js/magnific-popup/magnific-popup.min.js'],
        'datepicker' => ['/js/bootstrap-datepicker/bootstrap-datepicker3.min.css', '/js/bootstrap-datepicker/bootstrap-datepicker.min.js', '/js/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js'],
        'datetimepicker' => ['/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.css', '/js/bootstrap-datetimepicker/bootstrap-datetimepicker.min.js'],
        'colorpicker' => ['/js/bootstrap-colorpicker/bootstrap-colorpicker.min.css', '/js/bootstrap-colorpicker/bootstrap-colorpicker.min.js'],
        'confirm' => ['/js/jquery-confirm/jquery-confirm.min.css', '/js/jquery-confirm/jquery-confirm.min.js'],
        'select' => ['/js/bootstrap-select/bootstrap-select.min.css', '/js/bootstrap-select/bootstrap-select.min.js', '/js/bootstrap-select/i18n/defaults-zh_CN.min.js'],
        'fullcalendar' => [
            '/js/fullcalendar/core/main.min.css',
            '/js/fullcalendar/daygrid/main.min.css',
            '/js/fullcalendar/timegrid/main.min.css',
            '/js/fullcalendar/list/main.min.css',
            '/js/fullcalendar/core/main.min.js',
            '/js/fullcalendar/core/locales/zh-cn.js',
            '/js/fullcalendar/interaction/main.min.js',
            '/js/fullcalendar/daygrid/main.min.js',
            '/js/fullcalendar/timegrid/main.min.js',
            '/js/fullcalendar/list/main.min.js',
            '/js/fullcalendar/rrule/main.min.js',
        ],
        'treegrid' => [
            '/js/bootstrap-table/extensions/treegrid/jquery.treegrid.css',
            '/js/bootstrap-table/extensions/treegrid/jquery.treegrid.min.js',
            '/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js',
        ],
        'wizard' => ['/js/jquery.bootstrap.wizard.min.js'],
        'notify' => ['/js/bootstrap-notify.min.js'],
        'moment' => ['/js/momentjs/moment.min.js', '/js/momentjs/locale/zh-cn.min.js'],
        'maxlength' => ['/js/bootstrap-maxlength/bootstrap-maxlength.min.js'],
        'dropzone' => ['/js/dropzone/basic.min.css', '/js/dropzone/dropzone.min.css', '/js/dropzone/dropzone.min.js'],
        'cropper' => ['/js/cropper/cropper.min.css', '/js/cropper/cropper.min.js'],
        'dropdowntree' => ['/js/bootstrap-lyear-dropdowntree/bootstrap-lyear-dropdowntree.css', '/js/bootstrap-lyear-dropdowntree/bootstrap-lyear-dropdowntree.js'],
        'plupload' => [
            '/js/plupload-2.3.9/js/jquery.plupload.queue/css/jquery.plupload.queue.css',
            '/js/plupload-2.3.9/js/plupload.full.min.js',
            '/js/plupload-2.3.9/js/jquery.plupload.queue/jquery.plupload.queue.js',
            '/js/plupload-2.3.9/js/i18n/zh_CN.js',
        ],
        'wangEditor' => ['/js/wangEditor/wangEditor.min.js'],
        'ckeditor5' => ['/js/ckeditor5/ckeditor.js', '/js/ckeditor5/zh-cn.js'],
        'editormd' => ['/js/editor.md/css/editormd.min.css', '/js/editor.md/editormd.min.js'],
        'summernote' => ['/js/summernote/summernote-bs4.min.css', '/js/summernote/summernote-bs4.min.js', '/js/summernote/lang/summernote-zh-CN.min.js'],
        'tinymce' => ['/js/tinymce/tinymce.min.js'],
        'selectN' => ['/js/bootstrap-selectN.js'],
        'selectS' => ['/js/bootstrap-lyear-select/bootstrap-lyear-select.css', '/js/bootstrap-lyear-select/bootstrap-lyear-select.js'],
        'ZTree' => ['/js/zTree_v3/css/materialDesignStyle/materialdesign.css', '/js/zTree_v3/js/jquery.ztree.all.min.js'],
        'treeview' => ['/js/bootstrap-treeview/bootstrap-treeview.min.css', '/js/bootstrap-treeview/bootstrap-treeview.min.js'],
        'imgver' => ['/js/imgver/imgver.min.css', '/js/imgver/jquery.img.ver.min.js'],
        'verification' => ['/js/jquery.sliderVerification.min.js'],
        'touchspin' => [
            '/js/bootstrap-touchspin/jquery.bootstrap-touchspin.css',
            '/js/bootstrap-touchspin/jquery.bootstrap-touchspin.min.js',
            '/js/bootstrap-touchspin/numeral.min.js',
            '/js/bootstrap-touchspin/chs.min.js'
        ],
        'toolbar' => ['/js/jquery-toolbar/jquery-toolbar.min.css', '/js//jquery-toolbar/jquery.toolbar.min.js'],
        'fontIconPicker' => [
            '/js/fontIconPicker/jquery.fonticonpicker.min.css',
            '/js/fontIconPicker/jquery.fonticonpicker.bootstrap.min.css',
            '/js/fontIconPicker/jquery.fonticonpicker.min.js'
        ],
        'raty' => ['/js/raty/jquery.raty.js'],
        'rangeslider' => ['/js/ion-rangeslider/ion.rangeSlider.min.css', '/js/ion-rangeslider/ion.rangeSlider.min.js'],
        'sortable' => ['/js/drag-arrange.js'],
        'daterangepicker' => ['/js/bootstrap-daterangepicker/daterangepicker.css', '/js/bootstrap-daterangepicker/daterangepicker.min.js']
    ];
    $css = '';
    $script = '';
    $static = Config::get('xp.web_static');
    foreach ($arr as $value) {
        if (isset($js[$value])) {
            foreach ($js[$value] as $url) {
                $href = $static . $url;
                if (strpos($url, '.css') !== false) {
                    $css .= '<link rel="stylesheet" href="' . $href . '">' . "\n";
                }
                if (strpos($url, '.js') !== false) {
                    $script .= '<script src="' . $href . '"></script>' . "\n";
                }
            }
        }
    }
    if ($line) {
        return $css . "\n" . $script;
    }
    return ['css' => $css, 'js' => $script];
}

/**
 * 格式化字节大小
 * @param $size int 字节大小
 * @param int $precision 保留小数位
 * @return string 返回格式化后的大小
 */
function formatBytes($size, $precision = 2)
{
    if ($size > 0) {
        $base = log($size, 1024);
        $suffixes = array('Bytes', 'KB', 'MB', 'GB', 'TB');
        return round(pow(1024, $base - floor($base)), $precision) . ' ' . $suffixes[floor($base)];
    } else {
        return '0 Bytes';
    }
}

/**
 * 生成随机密码盐
 * @param int $length 密码长度
 * @return string 返回密码盐
 */
function generateSalt($length = 22)
{
    $salt = base64_encode(openssl_random_pseudo_bytes($length));
    $salt = str_replace('+', '.', $salt);
    return $salt;
}

/**
 * 替换图片路径
 * @param $content string 内容
 * @return string 返回替换后的内容
 */
function replaceImage($content)
{
    $pattern = '/<img\s+[^>]*src="([^"]+)"/i';
    return preg_replace_callback($pattern, function ($matches) {
        $newSrc = DOMAIN . $matches[1];
        return '<img src="' . $newSrc . '"';
    }, $content);
}

/**
 * 验证密码
 * @param $password string 密码
 * @param $storedHash string 存储的哈希值
 * @param $salt string 密码盐
 * @return bool 返回验证结果
 */
function verifyPassword($password, $storedHash, $salt)
{
    $hash = crypt($password, Config::get('xp.password_salt') . $salt);
    return $hash === $storedHash;
}

/**
 * 验证CORS
 * @param string $field 字段名(应用模块的配置)
 * @return bool 返回验证结果
 */
function verifyCors($field = 'site_url')
{
    if (isset($_SERVER['HTTP_ORIGIN'])) {
        $origin = parse_url($_SERVER['HTTP_ORIGIN']);
        $app = app('http')->getName();
        $site = parse_url(Meta::get($app, $field));
        if ($origin['host'] == $site['host']) {
            return true;
        }
    }
    return false;
}
